package org.jvm.nativemethod.methods.java.lang;

import org.jvm.nativemethod.NativeMethodRegister;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.*;
import org.jvm.rtda.thread.*;
import org.jvm.util.JvmUtil;

import java.util.Optional;

/**
 * @author 海燕
 * @date 2023/3/25
 */
public class ClassLoaderNativeMethod extends NativeMethodRegister {

    public static void init() throws NoSuchMethodException {
        register("java/lang/ClassLoader", "defineClass1", "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;", ClassLoaderNativeMethod.class.getDeclaredMethod("defineClass1", Frame.class));
        register("java/lang/ClassLoader", "findBootstrapClass", "(Ljava/lang/String;)Ljava/lang/Class;", ClassLoaderNativeMethod.class.getDeclaredMethod("findBootstrapClass", Frame.class));
        register("java/lang/ClassLoader", "findLoadedClass0", "(Ljava/lang/String;)Ljava/lang/Class;", ClassLoaderNativeMethod.class.getDeclaredMethod("findLoadedClass0", Frame.class));
    }

    /**
     * 使用this java类加载器进行类定义
     *
     * @param frame
     */
    public static void defineClass1(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object javaClassLoaderInstance = localVars.getThis();
        JavaKlassLoader javaKlassLoader = KlassLoaderRegister.getJavaKlassLoader(javaClassLoaderInstance);

        Object javaClassName = localVars.getRef(1);
        String klassName = JvmUtil.javaStringToJvmString(javaClassName).replace('.', '/');
        Object byteArr = localVars.getRef(2);
        int off = localVars.getInt(3);
        int len = localVars.getInt(4);
        byte[] srcData = byteArr.bytes();
        byte[] klassData = new byte[len];
        System.arraycopy(srcData, off, klassData, 0, len);
        Klass klass = javaKlassLoader.defineKlass(klassName, klassData, frame.getThread());
        Object jClass = Optional.ofNullable(klass).map(Klass::getjClass).orElse(null);
        frame.getOperandStack().pushRef(jClass);
    }

    /**
     * 请求根类加载器直接加载指定类
     *
     * @param frame
     */
    public static void findBootstrapClass(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object javaClassName = localVars.getRef(1);
        String klassName = JvmUtil.javaStringToJvmString(javaClassName).replace('.', '/');
        Klass klass = KlassLoaderRegister.getBootKlassLoader().loadKlass(klassName);
        Object jClass = Optional.ofNullable(klass).map(Klass::getjClass).orElse(null);
        frame.getOperandStack().pushRef(jClass);
    }

    /**
     * 寻找this java类加载器已经加载过得类
     *
     * @param frame
     */
    public static void findLoadedClass0(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object javaClassLoaderInstance = localVars.getThis();
        Object javaClassName = localVars.getRef(1);
        String klassName = JvmUtil.javaStringToJvmString(javaClassName).replace('.', '/');

        JavaKlassLoader javaKlassLoader = KlassLoaderRegister.getJavaKlassLoader(javaClassLoaderInstance);
        Klass loadedKlass = javaKlassLoader.findLoadedKlass(klassName);
        Object jClass = Optional.ofNullable(loadedKlass).map(Klass::getjClass).orElse(null);
        frame.getOperandStack().pushRef(jClass);
    }

}
